use ego_tree::NodeRef;

use futures::future::{BoxFuture, FutureExt};
use scraper::{ElementRef, Html, Node};
use common::compose::{make_empty_ctx, CallBox, CallCore, FError, ListHandler};


#[derive(Debug, thiserror::Error, PartialEq)]
pub enum ParseHtmlError {
    #[error("{0}")]
    InvalidHtml(String),
}
impl From<ParseHtmlError> for FError {
    fn from(err: ParseHtmlError) -> Self {
        match err {
            ParseHtmlError::InvalidHtml(msg) => FError::ParseError(format!("HTML解析错误: {}", msg)),
        }
    }
}



struct HtmlNode<'a> {
    node: NodeRef<'a, Node>,
}

impl<'a> HtmlNode<'a> {
    fn new_from_node(node: NodeRef<'a, Node>) -> Result<Self, ParseHtmlError> {
        Ok(Self {
            node
        })
    }

    fn new_from_str(html: String) -> Result<Self, ParseHtmlError> {
        let doc = Html::parse_document(&html);
        let root = doc.root_element().first_child().ok_or(ParseHtmlError::InvalidHtml("invalid html".to_string()))?;
        Ok(Self::new_from_node(root)?)
    }

    fn to_string(&self) -> Result<String, ParseHtmlError> {
        if self.node.value().is_text() {
            Ok(self.node.value().as_text().unwrap().to_string())
        }
        else if self.node.value().is_element() {
            Ok(ElementRef::wrap(self.node).unwrap().inner_html())
        }
        else {
            Err(ParseHtmlError::InvalidHtml("invalid node".to_string())) 
        }
    }

    fn token_length(&self) -> usize {
       todo!() 
    }

    fn get_children(self) -> Result<Vec<HtmlNode<'a>>, ParseHtmlError> {
        let data: Vec<HtmlNode<'a>> = self.node.children().map(|node| HtmlNode::new_from_str(node)).collect::<Result<Vec<HtmlNode>, ParseHtmlError>>()?;
        Ok(data)
    }

    fn is_text(&self) -> bool {
        self.node.value().is_text()
    }
}

const MAX_TOKEN_LENGTH: usize = 1024;

fn trans_node<'a>(node: &'a mut HtmlNode, translator: CallBox<'a, String, String>) -> BoxFuture<'a, Result<(), FError>> {
    async move {
        let ctx = make_empty_ctx();
        if node.token_length() < MAX_TOKEN_LENGTH {
            let translated_html = translator.run(node.to_string()?, ctx).await?;
            let translated_node = HtmlNode::new_from_str(translated_html);
            *node = translated_node;
            return Ok(());
        }

        if node.is_text() {
            let text = node.to_string();
            let split_texts = split_text(text, MAX_TOKEN_LENGTH);
            // 并行执行
            let handler = ListHandler::new(4, translator);
            let translated_texts = handler.run(split_texts, ctx).await?;
            let translated_text = translated_texts.join("");
            let translated_node = HtmlNode::new_from_str(translated_text);
            *node = translated_node;
            return Ok(());
        }

        let children = node.get_children();
        return trans_children(children, translator).await;
    }.boxed()
    
}

fn trans_children<'a>(mut children: Vec<&'a mut HtmlNode>, translator: CallBox<'a, String, String>) -> BoxFuture<'a, Result<(), FError>> {
    async move {
        let len = children.len();
        if len == 0 {
            return Ok(());
        }
    
        if len == 1 {
            let first = children.remove(0);
            trans_node(first, translator).await?;
            return Ok(());
        }
    
        let mut token_length = 0;
        let mut the_children = Vec::new();
        let mut the_rest = Vec::new();
        for child in children {
            if token_length + child.token_length() <= MAX_TOKEN_LENGTH {
                token_length += child.token_length();
                the_children.push(child);
            } else {
                the_rest.push(child);
            }
        }
    
        if the_children.len() > 0 {
            let (res1, res2) = futures::future::join(
                trans_children(the_children, translator.clone()),
                trans_children(the_rest, translator)
            ).await;
            res1?;
            res2?;
            return Ok(());
        }
    
        // 如果the_children.len() == 0，说明第一个节点已经超过了最大长度，需要特殊处理
        let first = the_rest.remove(0);
        let (res1, res2) = futures::future::join(
            trans_node(first, translator.clone()),
            trans_children(the_rest, translator)
        ).await;
        res1?;
        res2?;
        return Ok(());
    }.boxed()
}

fn split_text(text: String, max_length: usize) -> Vec<String> {
   todo!() 
}


#[cfg(test)]
mod tests {
    use scraper::ElementRef;

    use super::*;

    #[test]
    fn test_split_text() {
        let fragment = Html::parse_fragment("<h1>Hello, <i>world!</i></h1>");
        println!("root: {:?}", fragment.root_element().inner_html());
        let root = fragment.root_element().first_child().unwrap();
        let children = root.children();
        for child in children {
            let node = child.value();

            println!("is_text: {:?}", node.is_text());
            if node.is_text() {
                let text = node.as_text().unwrap().to_string();
                println!("text: {:?}", text);
            }
            else {
                let elem = node.as_element().unwrap();
                println!("elem: {:?}", elem.name());
                println!("node: {:?}", node_to_html(ElementRef::wrap(child).unwrap()));
            }
        }
    }
}